﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace TSTree.fuctions
{
   public class BlockFile
    {
        private const int BFHEAD_LENGTH = 8;
        private FileStream fp;
        private String filename;
        private int blocklength;
        private int act_block;
        private int offset = 0;

        public static int IOread = 0;
        public static int IOwrite = 0;

        private int number;  //number of blocks in file
        private Boolean new_flag;
        public BlockFile(String name, int b_length)
        {
            byte[] buffer;
            int l;

            filename = name;
            blocklength = b_length;
            number = 0;

            try
            {
                fp = new FileStream(name, FileMode.OpenOrCreate);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            if ((fp != null) && (fp.Length != 0) && (b_length == 0))
            // file already exists - read info from file
            {
                new_flag = false;
                blocklength = freadNumber();
                number = freadNumber();
            }
            else
            {
                if (blocklength < BFHEAD_LENGTH)
                {
                    Console.WriteLine("BlockFile::BlockFile: Blocks");
                }
                if (fp == null)
                    fp = new FileStream(name, FileMode.OpenOrCreate);

                new_flag = true;
                // write block file info
                fwriteNumber(blocklength);
                fwriteNumber(number);
                // fill the rest of the block with 0s
                buffer = new byte[l = blocklength - (int)fp.Position];
                for (int i = 0; i < l; i++)
                    buffer[i] = 0;
                putBytes(buffer, l);
            }
            fp.Seek(0, SeekOrigin.Begin);
            act_block = 0;
        }
        private int freadNumber()
        {
            ++IOread;

            byte[] number = new byte[4];
            fp.Read(number, 0, 4);

            return BitConverter.ToInt32(number, 0);
        }
        private void fwriteNumber(int value)
        {
            ++IOwrite;

            byte[] number = BitConverter.GetBytes(value);
            fp.Write(number, 0, 4);
        }
        private void putBytes(byte[] data, int size)
        {
            ++IOwrite;

            fp.Write(data, 0, size);
        }

        private void getBytes(byte[] data, int size)
        {
            ++IOread;

            fp.Read(data, 0, size);
        }
        private void seekBlock(int bnum)
        {
            fp.Seek((bnum - act_block) * blocklength + (int)fp.Position, SeekOrigin.Begin);
        }
        public void readHeader(byte[] buffer)
        {
            // read Rtree header
            fp.Seek(BFHEAD_LENGTH, SeekOrigin.Begin);
            getBytes(buffer, blocklength - BFHEAD_LENGTH);

            if (number < 1)
            {
                fp.Seek(0, SeekOrigin.Begin);
                act_block = 0;
            }
            else
                act_block = 1;
        }
        public void setHeader(byte[] header)
        {
            // write Rtree header to header
            //fp.Seek(BFHEAD_LENGTH, SeekOrigin.Current);
            fp.Seek(BFHEAD_LENGTH, SeekOrigin.Begin);

            putBytes(header, blocklength - BFHEAD_LENGTH);

            if (number < 1)
            {
                fp.Seek(0, SeekOrigin.Begin);
                act_block = 0;
            }
            else
                act_block = 1;
        }
        public virtual Boolean readBlock(byte[] b, int pos)
        {
            pos++;
            if (pos <= number && pos > 0)
                seekBlock(pos);
            else
                return false;

            getBytes(b, blocklength);

            if (pos + 1 > number)
            {
                fp.Seek(0, SeekOrigin.Begin);
                act_block = 0;
            }
            else
                act_block = pos + 1;

            return true;
        }

        public virtual Boolean writeBlock(byte[] block, int pos)
        {
            pos++;

            if (pos <= number && pos > 0)
                seekBlock(pos);
            else
                return false;

            putBytes(block, blocklength);
            if (pos + 1 > number)
            {
                fp.Seek(0, SeekOrigin.Begin);
                act_block = 0;
            }
            else
                act_block = pos + 1;
            return true;
        }

        public int appendBlock(byte[] block)
        {
            // go to the end of file
            fp.Seek(fp.Length, SeekOrigin.Begin);
            // write new block     
            putBytes(block, blocklength);
            // update number information (also write it to file)
            number++;
            fp.Seek(4, SeekOrigin.Begin);
            fwriteNumber(number);
            // position at the new inserted block
            fp.Seek((int)fp.Length - blocklength, SeekOrigin.Begin);

            // return the active block index
            return (act_block = number) - 1;
        }

        public Boolean deleteLastBlocks(int num)
        {
            FileStream tmp_fp;
            byte[] buffer;
            int blocks_read = 0;

            if (num > number)
                return false;

            fp.Seek(0, SeekOrigin.Begin);

            tmp_fp = new FileStream(filename + ".tmp", FileMode.OpenOrCreate);
            buffer = new byte[blocklength];
            int c = 0;
            while ((blocks_read < number - num + 1) && (c != -1))
            {
                if ((c = fp.Read(buffer, 0, buffer.Length)) != -1)
                {
                    tmp_fp.Write(buffer, 0, buffer.Length);
                    blocks_read++;
                }
            }
            fp.Close();

            fp = new FileStream(filename, FileMode.OpenOrCreate);
            c = 0; blocks_read = 0;
            tmp_fp.Seek(0, SeekOrigin.Begin);
            while ((blocks_read < number - num + 1) && (c != -1))
            {
                if ((c = tmp_fp.Read(buffer, 0, buffer.Length)) != -1)
                {
                    fp.Write(buffer, 0, c);
                    blocks_read++;
                }
            }
            tmp_fp.Close();

            number -= num;
            fp.Seek(BFHEAD_LENGTH, SeekOrigin.Begin);
            fwriteNumber(number);
            fp.Seek(0, SeekOrigin.Begin);
            act_block = 0;
            return true;
        }

        public Boolean fileNew()
        {
            return new_flag;
        }

        public int getBlocklength()
        {
            return blocklength;
        }

        public int getNumOfBlocks()
        {
            return number;
        }

        public string GetFileName()
        {
            return filename;
        }

        public void Close()
        {
            if (fp != null)
                fp.Close();
        }
    }
}
